#include <iostream>
#include <string>
#include <etcd/Client.hpp>
#include <etcd/Response.hpp>
#include <etcd/KeepAlive.hpp>
#include <thread>
#include <etcd/Watcher.hpp>
#include <functional>
#include "Logger.hpp"

namespace chatroom{
    // 服务注册类，向ETCD中放入键值对
    class Registery{
    public:
        using ptr = std::shared_ptr<Registery>;
        Registery(std::string& etcdhost):
        _client(std::make_shared<etcd::Client>(etcdhost)),
        _keep(_client->leasekeepalive(10).get()),
        _lease_id(_keep->Lease())
        {}
        ~Registery(){_keep->Cancel();}
        bool Register(const std::string& key,const std::string& value)
        {
            auto resp = _client->put(key,value,_lease_id).get();
            if(resp.is_ok()==false)
            {
                Logger_DEBUG("放入数据失败{}",resp.error_message());
                return false;
            }
            Logger_INFO("放入数据成功");
            return true;
        }
    private:
        // 客户端对象 保活租约对象 租约id
        std::shared_ptr<etcd::Client> _client;
        std::shared_ptr<etcd::KeepAlive> _keep;
        int64_t _lease_id;
    };

    // 注册发现类
    class Discovery{
        public:
            using ptr = std::shared_ptr<Discovery>;
            using NotifyCallback = std::function<void(std::string,std::string)>;
            Discovery(const string& etcdhost,const string& basedir,const NotifyCallback& put_cb,const NotifyCallback& del_cb)
            :_client(std::make_shared<etcd::Client>(etcdhost)),_put_cb(put_cb),_del_cb(del_cb)
            {
                // 1 先进行事件发现
                auto resp = _client->ls(basedir).get();
                if(resp.is_ok()==false)
                {
                    Logger_ERROR("这里发生了错误{}",resp.error_message());
                }
                // 对事件进行遍历
                int sz = resp.keys().size();
                for(int i = 0; i < sz;i++)
                {
                    if(_put_cb) _put_cb(resp.key(i),resp.value(i).as_string());
                }
                // 2 对事件进行监控
                _watcher = std::make_shared<etcd::Watcher>(*_client.get(),basedir,std::bind(&Discovery::callback,this,std::placeholders::_1),true);
            }
            ~Discovery(){
                _watcher->Cancel();
            }
        private:
            void callback(const etcd::Response &resp)
            {
                if(resp.is_ok()==false)
                {
                    Logger_ERROR("监控键值对失败{}",resp.error_message());
                    return;
                }
                for(auto const& event : resp.events())
                {
                    if(event.event_type() == etcd::Event::EventType::PUT)
                    {
                        if(_put_cb) _put_cb(event.kv().key(),event.kv().as_string());
                        Logger_INFO("上线服务{}-{}",event.kv().as_string(),event.kv().key());
                    }else if(event.event_type() == etcd::Event::EventType::DELETE_){
                        if(_del_cb) _del_cb(event.prev_kv().key(),event.prev_kv().as_string());
                        Logger_INFO("下线服务:{}-{}",event.prev_kv().as_string(),event.prev_kv().key());
                    }
                }
            }
        private:
            NotifyCallback _put_cb;
            NotifyCallback _del_cb;
            std::shared_ptr<etcd::Client> _client;
            std::shared_ptr<etcd::Watcher> _watcher;
    };
}